#
# Copyright (c) 2023 supercell
#
# SPDX-License-Identifier: BSD-3-Clause
#

module Luce
  # The spec of GFM about footnotes is [missing](https://github.com/github/cmark-gfm/issues/283#issuecomment-1378868725).
  # For online source code of cmark-gfm, see [master@c32ef78](https://github.com/github/cmark-gfm/blob/c32ef78/src/blocks.c#L1212).
  # A Rust implementation is also [available](https://github.com/wooorm/markdown-rs/blob/2498e31eecead798efc649502bbf5f86feaa94be/src/construct/gfm_footnote_definition.rs).
  # Footnote definition could contain multiple line-children and children could
  # be separated by one empty line.
  # Its first child-line would be the remaining part of the first line after
  # taking definition leading, combining with other child lines parsed by
  # `#parse_child_lines`, is fed into `BlockParser`.
  class FootnoteDefSyntax < BlockSyntax
    # Patterns that would be used to decide if one line is a block.
    @excluding_pattern = [
      Luce.empty_pattern,
      Luce.dummy_pattern,
    ]

    def pattern : Regex
      Luce.footnote_pattern
    end

    def parse(parser : BlockParser) : Node
      current = parser.current.content
      # ameba:disable Lint/NotNil
      match = pattern.match(current).not_nil! # firstMatch
      label = match[2]
      refs = parser.document.footnote_references
      refs[label] = 0

      id = DartURI.encode_component(label)
      parser.advance
      lines = [Line.new(current[match[0].size...])]
      lines.concat(parse_child_lines(parser))
      children = BlockParser.new(lines, parser.document).parse_lines
      element = Element.new("li", children)
      element.attributes["id"] = "fn-#{id}"
      element.footnote_label = label
      element
    end

    def parse_child_lines(parser : BlockParser) : Array(Line)
      children = [] of String
      # As one empty line should not split footnote definitions, use this flag
      should_be_block = false
      syntax_list = parser.block_syntaxes.select { |syntax| !@excluding_pattern.includes?(syntax.pattern) }

      # Every line is footnote's children until two blank lines or a block
      while !parser.done?
        line = parser.current.content
        if line.strip.empty?
          children << line
          parser.advance
          should_be_block = true
          next
        elsif line.starts_with?("    ")
          children << line[4..]
          parser.advance
          should_be_block = false
        elsif should_be_block || _is_block?(syntax_list, line)
          break
        else
          children << line
          parser.advance
        end
      end

      children.map { |e| Line.new(e) }
    end

    # Whether this line is any kind of block.
    # If `true`, the footnote block should end.
    private def _is_block?(syntax_list : Array(BlockSyntax), line : String) : Bool
      syntax_list.any?(&.pattern.matches?(line))
    end
  end
end
